package cn.xie.blog.servergateway.filler;

import cn.hutool.core.util.StrUtil;
import cn.xie.blog.model.common.enums.response.ResponseCodeEnum;
import cn.xie.blog.model.common.ttl.UserContext;
import cn.xie.blog.serverutil.constant.common.RequestConstant;
import cn.xie.blog.serverutil.constant.redis.RedisConstant;
import cn.xie.blog.serverutil.exception.BlogException;
import cn.xie.blog.serverutil.util.JwtUtil;
import cn.xie.blog.serverutil.util.RedisUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author: xiaoxie
 * create: 2025-04-30 11:31
 * @BelongsProject: blog-parent
 * @BelongsPackage: cn.xie.blog.servergateway.filler
 * description: token 过滤器
 */
@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Resource
    private JwtUtil jwtUtil;

    @Autowired
    private RedisUtil redisUtil;

    // 放行的路径列表
    private static final List<String> WHITE_LIST = List.of(
            "/web/admin/login",
            "/web/user/login",
            "/web/user/register",
            "/web/admin/getCode",
            "/web/user/getCode"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String requestPath = request.getPath().toString();

        // 放行特定的路径
        if (isWhiteListPath(requestPath)) {
            log.info("放行 path: {}", requestPath);
            return chain.filter(exchange);
        }

        // 获取 Token
        String token = request.getHeaders().getFirst("Authorization");
        if (token == null) {
            return setUnauthorizedResponse(exchange, "请求缺少 Authorization 头或格式不正确");
        }

        try {
            // 校验 Token
            String username = jwtUtil.getUserNameFromToken(token);
            validateToken(username, token);
            log.info("解析 Token 成功，用户名为: {}", username);
            String userContextFromToken = jwtUtil.getUserContextFromToken(token);
            log.info("解析 Token 成功，用户上下文为: {}", userContextFromToken);
            // 将 userContextFromToken json 数据存储到redis中
            String key = "admin".equalsIgnoreCase(username) ? RedisConstant.ADMIN_Context_KEY_PREFIX + username
                            : RedisConstant.USER_Context_KEY_PREFIX + username;
            redisUtil.put(key, userContextFromToken,RedisConstant.LOGIN_CODE_TIMEOUT);
            // 设置用户身份到 Header
            // 确保在同一个线程中使用 UserContext
            // 清理 UserContext
            return chain.filter(setUserToHeader(exchange, username));
        } catch (Exception e) {
            log.warn("Token 校验失败: {}", e.getMessage());
            return setUnauthorizedResponse(exchange, e.getMessage());
        }
    }

    // 校验 Token
    private void validateToken(String username, String token) {
        if (StrUtil.equalsIgnoreCase(username, "admin")) {
            validateAdminToken(username, token);
        } else {
            validateUserToken(username, token);
        }
    }

    // 校验 admin 用户的 Token
    private void validateAdminToken(String username, String token) {
        String redisKey = RedisConstant.ADMIN_LOGIN_KEY_PREFIX + "admin";
        if (!redisUtil.hasKey(redisKey)) {
            log.warn("admin token 已过期");
            throw new BlogException(ResponseCodeEnum.TOKEN_EXIST);
        }
        String redisToken = (String) redisUtil.get(redisKey);
        if (!jwtUtil.getUserNameFromToken(token).equals(username)) {
            log.warn("admin token 校验失败");
            throw new BlogException(ResponseCodeEnum.TOKEN_ERROR);
        }
    }

    // 校验普通用户的 Token
    private void validateUserToken(String username, String token) {
        String redisKey = RedisConstant.USER_LOGIN_KEY_PREFIX + username;
        if (!redisUtil.hasKey(redisKey)) {
            log.warn("{} token 已过期", username);
            throw new BlogException(ResponseCodeEnum.TOKEN_EXIST);
        }
        String redisToken = (String) redisUtil.get(redisKey);
        if (!jwtUtil.getUserNameFromToken(token).equals(username)) {
            log.warn("{} token 校验失败", username);
            throw new BlogException(ResponseCodeEnum.TOKEN_ERROR);
        }
    }

    // 判断请求路径是否在白名单中
    private boolean isWhiteListPath(String requestPath) {
        return WHITE_LIST.stream().anyMatch(path -> path.equals(requestPath));
    }

    // 设置用户身份到请求头中
    private ServerWebExchange setUserToHeader(ServerWebExchange exchange, String username) {
        return exchange.mutate()
                .request(exchange.getRequest().mutate()
                        .header(RequestConstant.HEAD_NAME, username)
                        .build())
                .build();
    }

    // 设置未授权响应
    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String message) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(message.getBytes(StandardCharsets.UTF_8));
        log.warn("未授权的请求: {}", message);
        return exchange.getResponse().writeWith(Mono.just(buffer)).then();
    }

    @Override
    public int getOrder() {
        return -2;
    }
}